package com.xiashitech.agent.instrumentation.base;

import com.xiashitech.agent.cache.status.CacheStatus;
import com.xiashitech.agent.instrumentation.utils.BaggageUtil;
import com.xiashitech.agent.utils.DurationPrinter;
import com.xiashitech.agent.utils.SessionUtil;
import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Context;

import javax.annotation.Nullable;

/**
 * 插桩执行前的准备参数对象
 * @author zhaoyk
 */
public class InstPreparedArgs {

    private Context context;

    private CallDepth callDepth;

    private DurationPrinter durationPrinter;

    public InstPreparedArgs(CallDepth callDepth) {
        this(null, callDepth);
    }

    public InstPreparedArgs(Context context, CallDepth callDepth) {
        this.context = context;
        this.callDepth = callDepth;
    }

    public Context getContext() {
        return context;
    }

    public Baggage getBaggage() {
        return Baggage.fromContext(context);
    }

    public void printElapseDuration() {
        if(durationPrinter != null)
            durationPrinter.printElapseDuration();
    }

    public void printElapseDuration(String posInfo) {
        if(durationPrinter != null)
            durationPrinter.printElapseDuration(posInfo);
    }

    /**
     * 插桩执行前的判断和准备操作
     * @param instInfo 插桩点名称，调式信息用
     * @param isGetUserId 优化参数，是否为获取userId的插桩，为true时，如果已获取到userId，那么插桩将不会执行
     * @param callDepthClass 调用层级判断参数
     * @return 预备参数对象
     */
    public static InstPreparedArgs prepareOnEnter(String instInfo, boolean isGetUserId, Class<?> callDepthClass) {
        if(CacheStatus.cannotRun())
            return null;

        CallDepth callDepth = null;
        if(callDepthClass != null) {
            callDepth = CallDepth.forClass(callDepthClass);
            if (callDepth.getAndIncrement() > 0)
                return new InstPreparedArgs(callDepth);
        }

        Baggage baggage = BaggageUtil.getCurrentBaggage();
        if(isGetUserId && SessionUtil.hasUserInBaggage(baggage))
            return callDepth != null ? new InstPreparedArgs(callDepth) : null;

        Context context = baggage.storeInContext(Java8BytecodeBridge.currentContext());
        InstPreparedArgs preparedArgs = new InstPreparedArgs(context, callDepth);

        if (InstrumentationConfig.get().getBoolean("xs.profile.log", false))
            preparedArgs.durationPrinter = new DurationPrinter(instInfo);

        return preparedArgs;
    }

    @Nullable
    public static InstPreparedArgs prepareOnEnter(String instInfo, boolean getUserId) {
        return prepareOnEnter(instInfo, getUserId, null);
    }

    @Nullable
    public static InstPreparedArgs prepareOnEnter(String instInfo, Class<?> callDepthClass) {
        return prepareOnEnter(instInfo, false, callDepthClass);
    }

    @Nullable
    public static InstPreparedArgs prepareOnEnter(String instInfo) {
        return prepareOnEnter(instInfo, false, null);
    }

    /**
     * 检查插桩的onEnter是否需要执行
     */
    public static boolean checkOnEnter(InstPreparedArgs preparedArgs) {
        if(preparedArgs == null)
            return false;

        if(preparedArgs.context == null)
            return false;

        return true;
    }

    /**
     * 检查插桩的onExit是否需要执行
     */
    public static boolean checkOnExit(InstPreparedArgs preparedArgs) {
        if(preparedArgs == null)
            return false;

        if(preparedArgs.callDepth != null && preparedArgs.callDepth.decrementAndGet() > 0)
            return false;

        return true;
    }

}
